package org.amphiprion.rfreplayanalyser.ui.lapanalyzer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import org.amphiprion.rfreplayanalyser.parser.DriverLocation;
import org.amphiprion.rfreplayanalyser.parser.Replay;
import org.amphiprion.rfreplayanalyser.parser.ReplayInfo;
import org.amphiprion.rfreplayanalyser.util.StringUtil;

@SuppressWarnings("serial")
public class LapStartEndAnalyerPanel extends JPanel {
	private static String[] gears = { "R", "N", "1", "2", "3", "4", "5", "6", "7" };

	private LapProvider lapProvider;
	private float[] startAnalyzerTime;
	private float[] endAnalyzerTime;
	private float[] replayTimeAtCarPosition;
	private BufferedImage wheel;

	public enum AnalyserMode {
		SPEED, GEAR, THROTTLE, STEER_YAW
	}

	public LapStartEndAnalyerPanel(final LapProvider lapProvider) {
		this.lapProvider = lapProvider;
		try {
			wheel = ImageIO.read(LapStartEndAnalyerPanel.class.getResourceAsStream("/org/amphiprion/rfreplayanalyser/ui/lapanalyzer/steering_wheel.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void analyzerRangeUpdated(float[] startAnalyzerTime, float[] endAnalyzerTime) {
		this.startAnalyzerTime = startAnalyzerTime;
		this.endAnalyzerTime = endAnalyzerTime;
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		if (startAnalyzerTime == null || endAnalyzerTime == null) {
			return;
		}
		if (startAnalyzerTime.length != lapProvider.getLapItems().size()) {
			return;
		}
		if (endAnalyzerTime.length != lapProvider.getLapItems().size()) {
			return;
		}
		if (lapProvider.getAnalyzerMode() == AnalyserMode.SPEED) {
			paintUsingReplayVelocity(g);
		} else if (lapProvider.getAnalyzerMode() == AnalyserMode.THROTTLE) {
			paintUsingReplayThrottle(g);
		} else if (lapProvider.getAnalyzerMode() == AnalyserMode.STEER_YAW) {
			paintUsingReplaySteerYaw(g);
		} else {
			paintUsingReplayGear(g);
		}

	}

	private void paintUsingReplayVelocity(Graphics g) {
		Replay replay = lapProvider.getReplay();
		ReplayInfo info = replay.getInfo();

		double vMin = Float.MAX_VALUE;
		double vMax = Float.MIN_VALUE;
		// System.out.println("$$$$$$$$$$$$$$$$$$$");
		int lapItemIndex = 0;
		for (LapItem lapItem : lapProvider.getLapItems()) {
			float lastReplayTime = 0;
			double lastV = 0;
			boolean first = true;
			List<DriverLocation> locs = replay.getDriverLocations(lapItem.driverId);
			for (DriverLocation loc : locs) {
				if (loc.getLap() == lapItem.lap) {
					double newV = Math.sqrt(loc.getVelX() * loc.getVelX() + loc.getVelY() * loc.getVelY() + loc.getVelZ() * loc.getVelZ());
					float newReplaytime = loc.getReplayTime();
					if (!first) {
						if (lastReplayTime <= startAnalyzerTime[lapItemIndex] && newReplaytime >= startAnalyzerTime[lapItemIndex]) {
							float ratio = (startAnalyzerTime[lapItemIndex] - lastReplayTime) / (newReplaytime - lastReplayTime);
							double interpolV = lastV + (newV - lastV) * ratio;
							if (interpolV > vMax) {
								vMax = interpolV;
							}
							if (interpolV < vMin) {
								vMin = interpolV;
							}
							// System.out.println(interpolV);
						} else if (lastReplayTime >= startAnalyzerTime[lapItemIndex] && newReplaytime <= endAnalyzerTime[lapItemIndex]) {
							if (lastV > vMax) {
								vMax = lastV;
							}
							if (lastV < vMin) {
								vMin = lastV;
							}
							// System.out.println(lastV);
						} else if (lastReplayTime <= endAnalyzerTime[lapItemIndex] && newReplaytime >= endAnalyzerTime[lapItemIndex]) {
							float ratio = (endAnalyzerTime[lapItemIndex] - lastReplayTime) / (newReplaytime - lastReplayTime);
							double interpolV = lastV + (newV - lastV) * ratio;
							if (interpolV > vMax) {
								vMax = interpolV;
							}
							if (interpolV < vMin) {
								vMin = interpolV;
							}

							// System.out.println(interpolV);
						}
					} else {
						first = false;
					}
					lastV = newV;
					lastReplayTime = newReplaytime;
				}

			}
			lapItemIndex++;
		}

		int marginLeft = 5;
		int marginRight = 5;
		int marginTop = 5;
		int marginBottom = 5;
		int graphWidth = getWidth() - marginLeft - marginRight;
		int graphHeight = getHeight() - marginTop - marginBottom;
		g.clipRect(marginLeft, marginTop, graphWidth, graphHeight + 1);

		vMin = 0;
		lapItemIndex = 0;
		for (LapItem lapItem : lapProvider.getLapItems()) {
			g.setColor(lapItem.color);
			float lastReplayTime = 0;
			double lastV = 0;
			boolean first = true;
			int lastPx = 0;
			int lastPy = 0;
			double ratioX = graphWidth / (endAnalyzerTime[lapItemIndex] - startAnalyzerTime[lapItemIndex]);
			double ratioY = graphHeight / (vMax - vMin);

			if (replayTimeAtCarPosition != null && replayTimeAtCarPosition.length > lapItemIndex) {
				int px = marginLeft + (int) ((replayTimeAtCarPosition[lapItemIndex] - startAnalyzerTime[lapItemIndex]) * ratioX);
				g.drawLine(px, marginTop, px, marginTop + graphHeight);
			}

			List<DriverLocation> locs = replay.getDriverLocations(lapItem.driverId);
			for (DriverLocation loc : locs) {
				if (loc.getLap() == lapItem.lap) {
					double newV = Math.sqrt(loc.getVelX() * loc.getVelX() + loc.getVelY() * loc.getVelY() + loc.getVelZ() * loc.getVelZ());
					float newReplaytime = loc.getReplayTime();
					int px = marginLeft + (int) ((loc.getReplayTime() - startAnalyzerTime[lapItemIndex]) * ratioX);
					int py = marginTop + graphHeight - (int) ((newV - vMin) * ratioY);
					if (!first) {

						g.drawLine(lastPx, lastPy, px, py);
					} else {
						first = false;
					}
					lastPx = px;
					lastPy = py;
					lastV = newV;
					lastReplayTime = newReplaytime;
				}
			}

			lapItemIndex++;
		}
	}

	private void paintUsingReplayGear(Graphics g) {
		Replay replay = lapProvider.getReplay();
		ReplayInfo info = replay.getInfo();

		int marginLeft = 20;
		int marginRight = 5;
		int marginTop = 5;
		int marginBottom = 5;
		int graphWidth = getWidth() - marginLeft - marginRight;
		int graphHeight = getHeight() - marginTop - marginBottom;

		int vMin = -1;
		int vMax = 7;
		double ratioY = (float) graphHeight / (vMax - vMin);
		for (int i = vMin; i <= vMax; i++) {
			int py = marginTop + graphHeight - (int) ((i - vMin) * ratioY);
			g.setColor(Color.LIGHT_GRAY);
			g.drawLine(marginLeft - 5, py, marginLeft + graphWidth, py);
			g.setColor(Color.DARK_GRAY);
			g.drawString(gears[i - vMin], 3, py + 4);
		}
		g.clipRect(marginLeft, marginTop, graphWidth, graphHeight + 1);

		int lapItemIndex = 0;
		for (LapItem lapItem : lapProvider.getLapItems()) {
			g.setColor(lapItem.color);
			float lastReplayTime = 0;
			double lastV = 0;
			boolean first = true;
			int lastPx = 0;
			int lastPy = 0;
			double ratioX = graphWidth / (endAnalyzerTime[lapItemIndex] - startAnalyzerTime[lapItemIndex]);

			if (replayTimeAtCarPosition != null && replayTimeAtCarPosition.length > lapItemIndex) {
				int px = marginLeft + (int) ((replayTimeAtCarPosition[lapItemIndex] - startAnalyzerTime[lapItemIndex]) * ratioX);
				g.drawLine(px, marginTop, px, marginTop + graphHeight);
			}

			List<DriverLocation> locs = replay.getDriverLocations(lapItem.driverId);
			for (DriverLocation loc : locs) {
				if (loc.getLap() == lapItem.lap) {
					double newV = loc.gear;
					float newReplaytime = loc.getReplayTime();
					int px = marginLeft + (int) ((loc.getReplayTime() - startAnalyzerTime[lapItemIndex]) * ratioX);
					int py = marginTop + graphHeight - (int) ((newV - vMin) * ratioY);
					if (!first) {

						g.drawLine(lastPx, lastPy, px, py);
					} else {
						first = false;
					}
					lastPx = px;
					lastPy = py;
					lastV = newV;
					lastReplayTime = newReplaytime;
				}
			}

			lapItemIndex++;
		}
	}

	private void paintUsingReplayThrottle(Graphics g) {
		Replay replay = lapProvider.getReplay();
		ReplayInfo info = replay.getInfo();

		int marginLeft = 40;
		int marginRight = 5;
		int marginTop = 7;
		int marginBottom = 7;
		int graphWidth = getWidth() - marginLeft - marginRight;
		int graphHeight = getHeight() - marginTop - marginBottom;

		int vMin = 0;
		int vMax = 31;
		double ratioY = (float) graphHeight / (vMax - vMin);
		for (int i = 0; i < 5; i++) {
			int value = Math.round(100 * (i / 4f * (vMax - vMin) / (vMax - vMin)));
			int py = marginTop + graphHeight - value * graphHeight / 100;
			g.setColor(Color.LIGHT_GRAY);
			g.drawLine(marginLeft - 5, py, marginLeft + graphWidth, py);
			g.setColor(Color.DARK_GRAY);
			g.drawString(StringUtil.padLeft(value + "%", 4), 3, py + 4);
		}

		g.clipRect(marginLeft, marginTop, graphWidth, graphHeight + 1);

		int lapItemIndex = 0;
		for (LapItem lapItem : lapProvider.getLapItems()) {
			g.setColor(lapItem.color);
			float lastReplayTime = 0;
			double lastV = 0;
			boolean first = true;
			int lastPx = 0;
			int lastPy = 0;
			double ratioX = graphWidth / (endAnalyzerTime[lapItemIndex] - startAnalyzerTime[lapItemIndex]);

			if (replayTimeAtCarPosition != null && replayTimeAtCarPosition.length > lapItemIndex) {
				int px = marginLeft + (int) ((replayTimeAtCarPosition[lapItemIndex] - startAnalyzerTime[lapItemIndex]) * ratioX);
				g.drawLine(px, marginTop, px, getHeight() - marginBottom);
			}

			List<DriverLocation> locs = replay.getDriverLocations(lapItem.driverId);
			for (DriverLocation loc : locs) {
				if (loc.getLap() == lapItem.lap) {
					double newV = loc.throttle;
					float newReplaytime = loc.getReplayTime();
					int px = marginLeft + (int) ((loc.getReplayTime() - startAnalyzerTime[lapItemIndex]) * ratioX);
					int py = marginTop + graphHeight - (int) ((newV - vMin) * ratioY);
					if (!first) {

						g.drawLine(lastPx, lastPy, px, py);
					} else {
						first = false;
					}
					lastPx = px;
					lastPy = py;
					lastV = newV;
					lastReplayTime = newReplaytime;
				}
			}

			lapItemIndex++;
		}
	}

	private void paintUsingReplaySteerYaw(Graphics g) {
		Replay replay = lapProvider.getReplay();
		ReplayInfo info = replay.getInfo();

		int marginLeft = 5;
		int marginRight = 5;
		int marginTop = 5;
		int marginBottom = 5;
		int graphWidth = getWidth() - marginLeft - marginRight;
		int graphHeight = getHeight() - marginTop - marginBottom;
		g.clipRect(marginLeft, marginTop, graphWidth, graphHeight + 1);

		double vMin = 0;
		double vMax = 127;

		int lapItemIndex = 0;
		for (LapItem lapItem : lapProvider.getLapItems()) {
			g.setColor(lapItem.color);
			float lastReplayTime = 0;
			double lastV = 0;
			boolean first = true;
			int lastPx = 0;
			int lastPy = 0;
			double ratioX = graphWidth / (endAnalyzerTime[lapItemIndex] - startAnalyzerTime[lapItemIndex]);
			double ratioY = graphHeight / (vMax - vMin);

			List<DriverLocation> locs = replay.getDriverLocations(lapItem.driverId);
			for (DriverLocation loc : locs) {
				if (loc.getLap() == lapItem.lap) {
					double newV = loc.steerYaw;
					float newReplaytime = loc.getReplayTime();
					int px = marginLeft + (int) ((loc.getReplayTime() - startAnalyzerTime[lapItemIndex]) * ratioX);
					int py = marginTop + (int) ((newV - vMin) * ratioY);
					if (!first) {
						g.drawLine(lastPx, lastPy, px, py);
					} else {
						first = false;
					}
					lastPx = px;
					lastPy = py;

					if (replayTimeAtCarPosition != null && replayTimeAtCarPosition.length > lapItemIndex) {
						if (newReplaytime >= replayTimeAtCarPosition[lapItemIndex] && lastReplayTime <= replayTimeAtCarPosition[lapItemIndex]) {
							int pwx = marginLeft + (int) ((replayTimeAtCarPosition[lapItemIndex] - startAnalyzerTime[lapItemIndex]) * ratioX);
							g.drawLine(pwx, 0, pwx, marginTop + graphHeight);

							if (endAnalyzerTime.length <= 2) {
								double v = lastV + (newV - lastV) * (replayTimeAtCarPosition[lapItemIndex] - lastReplayTime) / (newReplaytime - lastReplayTime);
								v -= 64;
								v *= 4;

								if (v == 0) {
									if (lapItemIndex == 0) {
										g.drawImage(wheel, px - 13, marginTop + graphHeight - 26, null);
									} else {
										g.drawImage(wheel, px - 13, marginTop, null);
									}

								} else {
									BufferedImageOp filter = new AffineTransformOp(AffineTransform.getRotateInstance(Math.toRadians(v), 13, 13), AffineTransformOp.TYPE_BICUBIC);

									if (lapItemIndex == 0) {
										g.drawImage(filter.filter(wheel, null), pwx - 13, marginTop + graphHeight - 26, null);
									} else {
										g.drawImage(filter.filter(wheel, null), pwx - 13, marginTop, null);
									}
								}
							}
						}

					}
					lastV = newV;
					lastReplayTime = newReplaytime;
				}
			}

			lapItemIndex++;
		}
	}

	public void updateReplayTimeForCars(float[] replayTimeAtCarPosition) {
		this.replayTimeAtCarPosition = replayTimeAtCarPosition;
	}
}
